package com.scs.application.modules.dept.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.scs.application.consts.GlobalConsts;
import com.scs.application.consts.SerialKeys;
import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.service.BaseServiceImpl;
import com.scs.application.core.utils.BeanUtils;
import com.scs.application.core.utils.DateUtils;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.core.utils.UserUtils;
import com.scs.application.modules.base.dto.ReagentTeamDTO;
import com.scs.application.modules.base.entity.Matr;
import com.scs.application.modules.base.entity.Warehouse;
import com.scs.application.modules.base.mapper.ReagentTeamMapper;
import com.scs.application.modules.base.service.MatrService;
import com.scs.application.modules.base.service.ReagentTeamService;
import com.scs.application.modules.dept.dto.ReturnReagentDTO;
import com.scs.application.modules.dept.dto.UseReagentDTO;
import com.scs.application.modules.dept.entity.DeptReturn;
import com.scs.application.modules.dept.entity.DeptReturnItem;
import com.scs.application.modules.dept.entity.UseReagent;
import com.scs.application.modules.dept.enums.UseReagentType;
import com.scs.application.modules.dept.mapper.UseReagentMapper;
import com.scs.application.modules.dept.service.DeptReturnItemService;
import com.scs.application.modules.dept.service.DeptReturnService;
import com.scs.application.modules.dept.service.UseReagentService;
import com.scs.application.modules.dept.util.UtilBudget;
import com.scs.application.modules.purchase.enums.PurchaseType;
import com.scs.application.modules.wm.dto.PickDTO;
import com.scs.application.modules.wm.entity.*;
import com.scs.application.modules.wm.enums.WorkOrderType;
import com.scs.application.modules.wm.mapper.PickItemMapper;
import com.scs.application.modules.wm.mapper.WorkOrderMapper;
import com.scs.application.modules.wm.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 试剂三级库使用明细 服务实现类
 * </p>
 */
@Slf4j
@Service
public class UseReagentServiceImpl extends BaseServiceImpl<UseReagentMapper, UseReagent> implements UseReagentService {

    @Resource
    private TertiaryStockService tertiaryStockService;

    @Resource
    private MatrService matrService;

    @Resource
    private WorkOrderService workOrderService;

    @Resource
    private WorkOrderItemService workOrderItemService;

    @Resource
    private ReagentTeamService reagentTeamService;

    @Resource
    private DeptReturnService deptReturnService;

    @Resource
    private DeptReturnItemService deptReturnItemService;

    @Resource
    private InOrderService inOrderService;

    @Resource
    private DistService distService;

    @Resource
    private DistItemService distItemService;

    @Resource
    private StockService stockService;

    @Resource
    private PickItemMapper pickItemMapper;

    @Override
    public UseReagentDTO getBySn(String stockId, UseReagent use) {
        // 获取当前用户
        String userName = UserUtils.currentUser().getName();
        TertiaryStock stock = tertiaryStockService.getBaseMapper().selectById(stockId);
        if (stock == null) {
            throw new BusinessException("未找到该试剂的可用库存，请重新扫描");
        }
        List<TertiaryStock> stockList = Collections.singletonList(tertiaryStockService.getBaseMapper().selectById(stockId));

        // 业务检测
        bizCheck(use, stockList);

        // 检查上一批次的试剂是否使用完毕
        List<UseReagentDTO> useReagentDTOS = checkLastLot(use, stockList);
        UseReagentDTO useReagent = useReagentDTOS.get(0);

        Matr matr = matrService.getById(stock.getMatrId());
        // 构建使用明细
        useReagent.setReagentTeamId(use.getReagentTeamId())
                .setDeptId(use.getDeptId())
                .setEquipmentCode(use.getEquipmentCode())
                .setUseType(use.getUseType())
                .setPersonPortion(matr.getPersonPortion())
                .setUseQty(1.0)
                .setUseStartTime(new Date())
                .setUser(userName)
                .setId(null);
        return useReagent;
    }

    @Override
    public List<UseReagentDTO> getBySns(List<String> stockIds, UseReagent use) {
        List<UseReagentDTO> result = new ArrayList<>();

        // 获取当前用户
        String userName = UserUtils.currentUser().getName();
        List<TertiaryStock> stockList = tertiaryStockService.getBaseMapper().selectBatchIds(stockIds);
        if (CollectionUtils.isEmpty(stockList)) {
            throw new BusinessException("未找到该试剂的可用库存，请重新扫描");
        }

        // 业务检测
        bizCheck(use, stockList);

        // 检查上一批次的试剂是否使用完毕
        List<UseReagentDTO> useReagentDTOS = checkLastLot(use, stockList);

        stockList.stream().map(TertiaryStock::getId).collect(Collectors.toList());
        Collection<Matr> matrs = matrService.listByIds(stockList.stream().map(TertiaryStock::getMatrId).collect(Collectors.toList()));
        useReagentDTOS.forEach(useReagent -> {
            Optional<Matr> matrOptional = matrs.stream().filter(matr -> matr.getId().equals(useReagent.getMatrId())).findFirst();
            if (matrOptional.isPresent()) {
                Matr matr = matrOptional.get();
                // 构建使用明细
                useReagent.setReagentTeamId(use.getReagentTeamId())
                        .setDeptId(use.getDeptId())
                        .setEquipmentCode(use.getEquipmentCode())
                        .setUseType(use.getUseType())
                        .setPersonPortion(matr.getPersonPortion())
                        .setUseQty(1.0)
                        .setUseStartTime(new Date())
                        .setUser(userName)
                        .setId(null);
                result.add(useReagent);
            }
        });
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveItems(List<UseReagent> items, List<String> stockIds) {
        List<String> matrIds = items.stream().map(UseReagent::getMatrId).collect(Collectors.toList());

        List<TertiaryStock> stockList = tertiaryStockService.getBaseMapper().selectBatchIds(stockIds);
        if (CollectionUtils.isEmpty(stockList)) { return; }
        // 扣减库存
        stockList.forEach(stock -> {
            stock.setPackageQty(0.0);
            stock.setSkuQty(0.0);
            stock.setAmount(0.0);
            stock.setMdate(new Date());
        });
        tertiaryStockService.saveOrUpdateBatch(stockList);

        // 更新上一批次的结束使用时间
        this.getBaseMapper().update(null, new UpdateWrapper<UseReagent>()
                .set("use_end_time", new Date()).set("is_end", true)
                .eq("reagent_team_id", items.get(0).getReagentTeamId())
                .eq("is_end", false)
                .in("matr_id", matrIds));
        // 保存试剂消耗明细
        this.saveBatch(items);
    }

    /**
     * 检查上一批次的试剂是否使用完毕
     */
    private List<UseReagentDTO> checkLastLot(UseReagent use, List<TertiaryStock> stockList) {
        List<UseReagentDTO> result = new ArrayList<>();
        for (TertiaryStock stock : stockList) {
            UseReagentDTO useReagent = new UseReagentDTO();
            BeanUtils.copyProperties(stock, useReagent);
            result.add(useReagent);
            useReagent.setStockId(stock.getId());
            useReagent.setNewFlag(false);
            if (UseReagentType.MANUAL.getVal().equals(use.getUseType()) || UseReagentType.EQUIPMENT.getVal().equals(use.getUseType())) {
                // 查询上一次试剂消耗记录
                // 使用max函数优化 SELECT * FROM dept_use_reagent WHERE id = ( select id from ( SELECT id, max(use_start_time) FROM dept_use_reagent WHERE reagent_team_id = '1467689650524045313' AND matr_id = '1473118132963999745') t )
                UseReagent lastUse = this.getOne(new QueryWrapper<UseReagent>()
                        .eq("reagent_team_id", use.getReagentTeamId())
                        .eq("matr_id", stock.getMatrId())
                        .orderByDesc("use_start_time")
                        .last("limit 1")
                );

                // 当前扫描试剂批次与上一次上机试剂批次不一致
                if (lastUse == null) {
                    // 批次为新批次，第一次使用
                    useReagent.setNewFlag(true);
                } else if (!lastUse.getLot().equals(stock.getLot())) {
                    TertiaryStock oldStock = tertiaryStockService.getBaseMapper().selectOne(new QueryWrapper<TertiaryStock>()
                            .eq("reagent_team_id", use.getReagentTeamId())
                            .eq("matr_id", lastUse.getMatrId())
                            .eq("lot", lastUse.getLot())
                            .gt("package_qty", 0)
                            .last("limit 1")
                    );
                    if (oldStock != null) {
                        throw new BusinessException(String.format("%s的批次为新批次，系统检测到还有旧的批次%s未使用完，请先使用旧批次", stock.getMatrName(), oldStock.getLot()));
                    } else {
                        // 批次为新批次，旧批次试剂已使用完
                        useReagent.setNewFlag(true);
                    }
                }
            }
        }
        return result;
    }

    /**
     * 通用业务检测
     */
    private void bizCheck(UseReagent use, List<TertiaryStock> stockList) {
        for (TertiaryStock stock : stockList) {
            if (!UseReagentType.EXPIRE.getVal().equals(use.getUseType()) && DateUtils.getDateDiffOfDays(DateUtils.now(), stock.getExpDate()) < 0) {
                throw new BusinessException(String.format("试剂已过期，请重新扫描！品名：%s，SN码：%s", stock.getMatrName(), stock.getSn()));
            }
        }

    }

    /**
     * 试剂调拨
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void transfer(List<UseReagent> items, List<String> stockIds, ReturnReagentDTO returnReagentDTO, Warehouse warehouse) {
        fillReturnReagentDTO(returnReagentDTO);

        List<TertiaryStock> stockList = tertiaryStockService.getBaseMapper().selectBatchIds(stockIds);

        // 删除三级库库存
        tertiaryStockService.removeByIds(stockIds);

        // 创建科室退库单、二级库配送单
        createOrder(stockList, returnReagentDTO, warehouse);

    }

    private void createOrder(List<TertiaryStock> stockList, ReturnReagentDTO dto, Warehouse warehouse) {
        List<String> snList = stockList.stream().map(TertiaryStock::getSn).collect(Collectors.toList());

        // 查找原始科室配送的出库单
        List<WorkOrder> oldWorkOrder = ((WorkOrderMapper) workOrderService.getBaseMapper()).getOrderBySN(snList, WorkOrderType.OUT_SHIFT, dto.getFromReagentTeamId());
        List<String> workOrderIds = oldWorkOrder.stream().map(WorkOrder::getId).collect(Collectors.toList());
        // 查找原始科室配送的出库单明细
        List<WorkOrderItem> workOrderItemList = workOrderItemService.list(new QueryWrapper<WorkOrderItem>().in("work_order_id", workOrderIds).in("sn", snList));

        // 退货总金额
        double sumAmount = stockList.stream().mapToDouble(TertiaryStock::getAmount).sum();

        // 生成退库单
        List<WorkOrder> returnOrder = createReturnOrder(stockList, workOrderItemList, dto, warehouse, sumAmount);

        //生成配送单
        createDistOrder(returnOrder, snList, dto, warehouse, sumAmount);
    }

    private List<WorkOrder> createReturnOrder(List<TertiaryStock> stockList, List<WorkOrderItem> workOrderItemList, ReturnReagentDTO dto, Warehouse warehouse, double sumAmount) {
        // 生成退库单
        DeptReturn deptReturn = new DeptReturn();
        deptReturn.setDeptId(dto.getFromDeptId())
                .setDeptName(dto.getToDeptName())
                .setBusKey(serialRuleAdapter.getSerialRuleService().generateCode(SerialKeys.DEPT_RETURN_KEY))
                .setAmount(sumAmount)
                .setBusUser(UserUtils.currentUser().getName())
                .setBusDate(Calendar.getInstance().getTime())
                .setWarehouseId(warehouse.getApplyWarehouseId())
                .setWarehouseName(warehouse.getApplyWarehouseName())
                .setFlowModified(LocalDateTime.now()).setFlowModifier(UserUtils.currentUser().getName()).setFlowStatus(GlobalConsts.FLOW_END_VALUE)
        ;
        deptReturnService.save(deptReturn);

        // 生成退库单明细
        List<DeptReturnItem> returnItemList = new ArrayList<>();
        stockList.forEach(stock -> {
            DeptReturnItem item = new DeptReturnItem();
            BeanUtils.copyProperties(stock, item, "id", "modifier", "gmtModified");
            WorkOrderItem workOrderItem = workOrderItemList.stream().filter(woi -> stock.getSn().equals(woi.getSn())).findAny().get();
            item.setDeptReturnId(deptReturn.getId()).setStockId(workOrderItem.getStockId()).setLineId(workOrderItem.getLineId());
            returnItemList.add(item);
        });
        deptReturnItemService.saveBatch(returnItemList);

        List<WorkOrder> workOrderList = inOrderService.createWorkOrderByDeptReturn(deptReturn);

        if (workOrderList == null || workOrderList.size() == 0) {
            throw new BusinessException("转换退库单失败");
        }
        // 提交出库单
        workOrderService.postBatch(workOrderList.stream().map(WorkOrder::getId).collect(Collectors.toList()));
        log.info("三级库退库完成，共生成{}张入库单" , workOrderList.size());
        return workOrderList;
    }

    private void createDistOrder(List<WorkOrder> workOrderList, List<String> snList, ReturnReagentDTO dto, Warehouse warehouse, double sumAmount) {
        // 查找退库后的出库单
        List<String> workOrderIds = workOrderList.stream().map(WorkOrder::getId).collect(Collectors.toList());
        // 查找退库后的出库单明细
        List<WorkOrderItem> workOrderItems = workOrderItemService.list(new QueryWrapper<WorkOrderItem>().in("work_order_id", workOrderIds).in("sn", snList));

        //生成配送单
        Dist dist = new Dist();
        dist.setBusKey(serialRuleAdapter.getSerialRuleService().generateCode(SerialKeys.WM_DIST_KEY))
                .setBusDate(new Date()).setBusUser(UserUtils.currentUser().getName())
                .setDeptId(dto.getToDeptId()).setDeptName(dto.getToDeptName())
                .setWarehouseId(warehouse.getApplyWarehouseId()).setWarehouseName(warehouse.getApplyWarehouseName())
                .setAmount(sumAmount)
                .setReagentTeamId(dto.getToReagentTeamId())
                .setDistType(PurchaseType.REAGENT.getKey())
                .setStatusPick(2).setPicker(UserUtils.currentUser().getName()).setPickDate(new Date())
                .setFlowModified(LocalDateTime.now()).setFlowModifier(UserUtils.currentUser().getName()).setFlowStatus(GlobalConsts.FLOW_END_VALUE)
//                .setRemark(String.format("--由入库单%s转换而来", workOrder.getBusKey()))
        ;
        distService.save(dist);

        for (WorkOrderItem workOrderItem : workOrderItems) {
            DistItem distItem = new DistItem();
            distItem.setDistId(dist.getId())
                    .setSkuPrice(workOrderItem.getSkuPrice())
                    .setMatrId(workOrderItem.getMatrId()).setMatrName(workOrderItem.getMatrName()).setMatrSpec(workOrderItem.getMatrSpec())
                    .setPackageUnit(workOrderItem.getPackageUnit()).setPackageQty(workOrderItem.getPackageQty())
                    .setSkuUnit(workOrderItem.getSkuUnit()).setSkuPrice(workOrderItem.getSkuPrice()).setSkuQty(workOrderItem.getSkuQty())
                    .setPickedSkuQty(0.0)
                    .setPackageRate(workOrderItem.getSkuQty() / workOrderItem.getPackageQty())
                    .setAmount(workOrderItem.getAmount())
//                    .setRemark(String.format("由入库单%s转换而来", workOrder.getBusKey()))
            ;
            distItemService.save(distItem);
        }

        // 增加拣货明细
        List<PickDTO> pickList = new ArrayList<>();
        snList.forEach(sn -> {
            PickDTO pickDTO = new PickDTO();
            pickDTO.setDistId(dist.getId());
            pickDTO.setUniqueCode(sn);
            pickList.add(pickDTO);
        });
        distService.pickBatch(pickList);

        List<PickItem> pickItems = pickItemMapper.selectList(Wrappers.<PickItem>query().eq("dist_id", dist.getId()));
        if (CollectionUtils.isEmpty(pickItems)) {
            throw new BusinessException("拣货失败：拣货单明细为空");
        }
        //预算控制检查
        UtilBudget.checkBudgetControl(dist.getDeptId(),pickItems);
        // 转换配送单
        List<WorkOrder> wos = distService.convertDistToOrders(dist);
        // 提交配送单
        workOrderService.postBatch(wos.stream().map(WorkOrder::getId).collect(Collectors.toList()));
        log.info("三级库配送完成，共生成{}张入库单" , workOrderList.size());
    }

    /**
     * 填充调拨科室
     */
    private void fillReturnReagentDTO(ReturnReagentDTO dto) {
        ReagentTeamMapper reagentTeamMapper = (ReagentTeamMapper) reagentTeamService.getBaseMapper();
        if (StringUtils.isNotEmpty(dto.getToReagentTeamId())) {
            ReagentTeamDTO toDept = reagentTeamMapper.getDeptById(dto.getToReagentTeamId());
            if (toDept != null) {
                dto.setToDeptId(toDept.getDeptId());
                dto.setToDeptName(toDept.getDeptName());
            }
        }

        if (StringUtils.isNotEmpty(dto.getFromReagentTeamId())) {
            ReagentTeamDTO fromDept = reagentTeamMapper.getDeptById(dto.getFromReagentTeamId());
            if (fromDept != null) {
                dto.setFromDeptId(fromDept.getDeptId());
                dto.setFromDeptName(fromDept.getDeptName());
            }
        }
    }

}
